package inferenceEngine.types;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

import org.dom4j.Element;
import org.dom4j.ElementHandler;

import common.Pair;

public class LinguisticValue {
	private ArrayList<Pair<Double, Double>> points = new ArrayList<Pair<Double, Double>>();
	private int distanceOfXString = 5;
	private String name = "";
	private Element ele = null;
	private Color color = Color.BLACK;
	private boolean required = false;
	private boolean recommended = false;
	private double step = 0.01;
	public LinguisticValue(Element ele) {
		this.ele = ele;
		name = ele.attributeValue("name");
		List<Element> list = ele.elements("point");
		for( Element e: list ){
			double x = Double.parseDouble( e.attributeValue("x") );
			double y = Double.parseDouble( e.attributeValue("y") );
			Pair<Double, Double> p = new Pair( x,y );
			addPoint(p);
		}
		Collections.sort( points, new Comparator<Pair<Double, Double>>() {

			@Override
			public int compare(Pair<Double, Double> o1, Pair<Double, Double> o2) {
				return o1.first > o2.first? 1 : -1;
			}

		});
	}
	
	private void addPoint( Pair<Double, Double> p ){
		for( int i=0; i<points.size() ; i++ ){
			if( points.get(i).first.equals( p.first ) ){
				points.remove(i);
			}
		}
		points.add(p);
	}
	
	public Color getColor(){
		return color;
	}
	
	public void drawLine( Graphics g, double minX, double maxX, Color c ){
		if( points.size() == 0 ){
			return;
		}
		Rectangle clip = g.getClipBounds();
		this.color = c;
		Point first = translateIntoClipPoint( points.get(0), minX, maxX, clip );
		int y = first.y;
		g.setColor(c);
		g.drawLine( 0, y, first.x, first.y);
		g.drawString( String.valueOf( points.get(0).first ), first.x-10 , (int)clip.getMaxY() - distanceOfXString );
		for( int i=1; i<points.size(); i++ ){
			Point p1 = translateIntoClipPoint( points.get(i-1) , minX, maxX, clip);
			Point p2 = translateIntoClipPoint( points.get(i), minX, maxX, clip);
			g.drawLine( p1.x, p1.y, p2.x, p2.y );
			g.drawString( String.valueOf( points.get(i).first ), p2.x-10 , (int)clip.getMaxY() - distanceOfXString );
		}
		Point last = translateIntoClipPoint( points.get( points.size()-1 ), minX, maxX, clip );
		g.drawLine( (int)clip.getMaxX(), last.y, last.x, last.y);
		g.drawString( String.valueOf( points.get( points.size()-1 ).first ), last.x-10 , (int)clip.getMaxY() - distanceOfXString );
	}
	
	public double getMaxX(){
		if( points.size()>0 ){
			return points.get( points.size()-1 ).first;
		}else{
			return 0;
		}
		
	}
	
	public double getMinX(){
		if( points.size()>0 ){
			return points.get( 0 ).first;
		}else{
			return 0;
		}
		
	}
	
	private Point translateIntoClipPoint( Pair<Double,Double> p , double minX, double maxX, Rectangle clip  ){
		double range = maxX - minX;
		double currentX = p.first - minX;
		double xPercent = currentX/range;
		double clipMaxY = clip.getMaxY() - 20;
		return new Point( (int) (  ( clip.getMaxX() - clip.getMinX() ) * xPercent ) 
									,   (int) ( clipMaxY - p.second*( clipMaxY -  clip.getMinY()  )  ));
	}

	public String getName() {
		return name;
	}
	
	public void addPoints( ArrayList<Pair<Double,Double>> list ){
//		points.addAll( list );
		for( Pair<Double,Double> p : list ){
			addPoint(p);
		}
		Collections.sort( points, new Comparator<Pair<Double, Double>>() {

			@Override
			public int compare(Pair<Double, Double> o1, Pair<Double, Double> o2) {
				return o1.first > o2.first? 1 : -1;
			}

		});
	}
	
	public void save(){
		ele.clearContent();
		ele.addAttribute("name", name);
		for(Pair<Double, Double> pair : points ){
			Element element = ele.addElement("point");
			element.addAttribute( "x", String.valueOf( pair.first ) );
			element.addAttribute( "y", String.valueOf( pair.second ) );
		} 
	}

	public String getParameter( String attribute ) {
		if( points.size() > 0 ){
			String results = "";
			if( points.get(0).second == 0 ){
				results += attribute + ">=" + String.valueOf( points.get(0).first );
				results += " and ";
			}
			for( int i=1; i<points.size(); i++ ){
				if( points.get(i-1).second == 0 && points.get(i).second == 0 ){
					results += attribute + ">=" + String.valueOf( points.get(i-1).first );
					results += " and ";
					results += attribute + "<=" + String.valueOf( points.get(i).first );
					results += " and ";
				}
			}
			if( points.get( points.size()-1 ).second == 0 ){
				results += attribute + "<=" + String.valueOf( points.get( points.size()-1 ).first );
			}
			if( results.endsWith( "and ") ){
				results = results.substring(0, results.length()-4 );
			}
			return results;
		}else{
			return "";
		}
	}

	public boolean isRequired() {
		return required;
	}

	public void setRequired(boolean required) {
		this.required = required;
	}

	public double getFuzzyValue( double x ) {
		int leftIndex = 0;
		int rightIndex = 0;
		for( int i = 0; i<points.size() ; i++ ){
			Pair<Double, Double> point = points.get(i);				
			if( x <= point.first ){
				rightIndex = i;			
				break;
			}else{
				leftIndex = i;
			}
		}
		if( rightIndex> leftIndex ){
			double leftX = points.get( leftIndex).first;
			double rightX = points.get( rightIndex ).first;
			double height = points.get( leftIndex).second > points.get( rightIndex ).second ?   
					points.get( leftIndex).second - points.get( rightIndex ).second :
						points.get( rightIndex ).second - points.get( leftIndex).second;
			return height / ( rightX - leftX ) * (x-leftX);
		}else if( leftIndex == points.size()-1 ){
			return points.get( leftIndex ).second;
		}else if( rightIndex == 0 ){
			return points.get( rightIndex ).second;
		}
		return 0;
	}

	public void reduceFuzzyValue(double x) {
		double value = getFuzzyValue(x);
		addPoint( new Pair<Double,Double>( x, value-step ) );
		save();
	}

	public boolean isRecommended() {
		return recommended;
	}
	
	public void setRecommended( boolean recommended ) {
		this.recommended = recommended;
	}


}
